package com.kylin.hibernate;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.List;

import org.apache.log4j.Logger;
import org.hibernate.Criteria;
import org.hibernate.FetchMode;
import org.hibernate.LockMode;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Example;
import org.hibernate.criterion.Expression;
import org.hibernate.criterion.LogicalExpression;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projection;
import org.hibernate.criterion.Restrictions;

import com.kylin.common.exception.BusinessException;
import com.kylin.common.exception.SystemException;
import com.kylin.module.shop.orm.Product;

/**
 * Data access object (DAO) for domain model
 * 
 * @author MyEclipse - Hibernate Tools
 */
public class BaseHibernateDAO {
	protected static Logger log = Logger.getLogger(BaseHibernateDAO.class);

public static void main(String[]s) throws Exception{
	Class parent = Product.class;
//	String nameStr = "company.user.country";
//	String lastValue ="zh_CN" ;
////	User user = (User) BaseHibernateDAO.findById(User.class, new Long(10L) );
//	Criteria criteria = getSession().createCriteria(parent);
//	;
//	List list = criteria.add( getRestrictionsIn(parent, nameStr,lastValue,"=") ).list();
//	for(int i=0;i<list.size();i++){
//		System.out.println(i+"  "+list.get(i));
//	}
	
	PropertyCondition[] pConditions = new PropertyCondition[] {
			new PropertyCondition("company.user.country","zh_CN",PropertyCondition.OPERATOR_EQ)
	};
	Criteria criteria = createCriteria(parent,pConditions);
	List list = criteria.list();
	for(int i=0;i<list.size();i++){
		System.out.println(i+"  "+list.get(i));
	}
}


	public static Session getSession() {
		return HibernateSessionFactory.getSession();
	}

	public static void tranSave(Object transientInstance) throws BusinessException {
		try {
			getSession().save(transientInstance);
		} catch (RuntimeException re) {
			if (re.getCause() instanceof java.sql.BatchUpdateException)
				throw new BusinessException(re.getCause().getMessage(), re.getCause());
			else
				throw new SystemException(re.getMessage(), re);
		}
	}

	public static void save(Object transientInstance) throws BusinessException {
		try {
			getSession().beginTransaction();
			getSession().save(transientInstance);
			getSession().getTransaction().commit();
			getSession().flush();
			getSession().clear();
		} catch (RuntimeException re) {
			if (re.getCause() instanceof java.sql.BatchUpdateException)
				throw new BusinessException(re.getCause().getMessage(), re.getCause());
			else
				throw new SystemException(re.getMessage(), re);
		}
	}

	public static Object saveRtObj(Object transientInstance) throws BusinessException {
		try {
			getSession().beginTransaction();
			Serializable id = getSession().save(transientInstance);
			getSession().getTransaction().commit();
			getSession().flush();
			getSession().clear();
			return findById( transientInstance.getClass(),new Long(id.toString()));
		} catch (RuntimeException re) {
			if (re.getCause() instanceof java.sql.BatchUpdateException)
				throw new BusinessException(re.getCause().getMessage(), re.getCause());
			else
				throw new SystemException(re.getMessage(), re);
		}
	}

	public static Serializable saveRtId(Object transientInstance) throws BusinessException {
		try {
			getSession().beginTransaction();
			Serializable id = getSession().save(transientInstance);
			getSession().getTransaction().commit();
			getSession().flush();
			getSession().clear();
			return id;
		} catch (RuntimeException re) {
			if (re.getCause() instanceof java.sql.BatchUpdateException)
				throw new BusinessException(re.getCause().getMessage(), re.getCause());
			else
				throw new SystemException(re.getMessage(), re);
		}
	}

	public static void saveOrUpdate(Object transientInstance) throws BusinessException {
		try {
			getSession().beginTransaction();
			getSession().saveOrUpdate(transientInstance);
			getSession().getTransaction().commit();
			getSession().flush();
			getSession().clear();
		} catch (RuntimeException re) {
			if (re.getCause() instanceof java.sql.BatchUpdateException)
				throw new BusinessException(re.getCause().getMessage(), re.getCause());
			else
				throw new SystemException(re.getMessage(), re);
		}
	}

	public static void update(Object transientInstance) throws BusinessException {
		try {
			getSession().beginTransaction();
			getSession().update(transientInstance);
			getSession().getTransaction().commit();
			getSession().flush();
			getSession().clear();
		} catch (RuntimeException re) {
			if (re.getCause() instanceof java.sql.BatchUpdateException)
				throw new BusinessException(re.getCause().getMessage(), re.getCause());
			else
				throw new SystemException(re.getMessage(), re);
		}
	}

	public static void delete(Object persistentInstance) throws BusinessException {
		try {
			getSession().beginTransaction();
			getSession().delete(persistentInstance);
			getSession().flush();
			getSession().clear();
			getSession().getTransaction().commit();
			getSession().clear();		
		} catch (RuntimeException re) {
			if (re.getCause() instanceof java.sql.BatchUpdateException)
				throw new BusinessException(re.getCause().getMessage(), re.getCause());
			else
				throw new SystemException(re.getMessage(), re);
		}
	}

	public static void deleteById(Class clz,java.lang.Long id) throws BusinessException {
		try {
			getSession().beginTransaction();
			getSession().delete( findById( clz,id) );
			getSession().flush();
			getSession().clear();
			getSession().getTransaction().commit();
			//getSession().close();
		} catch (RuntimeException re) {
			if (re.getCause() instanceof java.sql.BatchUpdateException)
				throw new BusinessException(re.getCause().getMessage(), re.getCause());
			else
				throw new SystemException(re.getMessage(), re);
		}
	}

	/**
	 * @param sql
	 *<br>        "select user from users";
	 *<br>		  "delete user  ";
	 * @param pc  
	 * <br>      user.id = 2  
	 * @return
	 * @throws BusinessException
	 */
	public static int  execHql(String sql,PropertyCondition[]pc) throws BusinessException{
        
		int rs = -1;
		try {
			getSession().beginTransaction();
			for(int i=0;i<pc.length;i++){
				if(i == 0)
					sql += " where ";
				else
					sql += " and ";
				sql += pc[i].getPropertyName() + pc[i].getRelationalOperator() + "?";
			}
			Query query = getSession().createQuery(sql);
			for(int i=0;i<pc.length;i++){
				query.setParameter(pc[i].getPropertyName(), pc[i].getPropertyValue());
			}
			rs = query.executeUpdate();
			getSession().flush();
			getSession().clear();
			getSession().getTransaction().commit();
			//getSession().close();
		} catch (RuntimeException re) {
			if (re.getCause() instanceof java.sql.BatchUpdateException)
				throw new BusinessException(re.getCause().getMessage(), re.getCause());
			else
				throw new SystemException(re.getMessage(), re);
		}
		return rs;	
	}

	/**
	 * @param sql
	 * <br>      update Product set active=1 where id=3 or id=6
	 * @return
	 * @throws BusinessException
	 */
	public static int  execHql(String sql) throws BusinessException{
     log.debug("execHql: "+ sql);   
		int rs = -1;
		try {
			getSession().beginTransaction();
			Query query = getSession().createQuery(sql);
			rs = query.executeUpdate();
			getSession().flush();
			getSession().clear();
			getSession().getTransaction().commit();
			//getSession().close();
		} catch (RuntimeException re) {
			if (re.getCause() instanceof java.sql.BatchUpdateException)
				throw new BusinessException(re.getCause().getMessage(), re.getCause());
			else
				throw new SystemException(re.getMessage(), re);
		}
		return rs;	
	}

	public static Object findById(Class clz, java.lang.Long id) throws BusinessException {
		log.debug("findById("+clz.getName()+","+id+")");
		getSession().clear();
		try {
			Object instance = getSession().get(clz, id);
			return instance;
		} catch (RuntimeException re) {
			if (re.getCause() instanceof java.sql.BatchUpdateException)
				throw new BusinessException(re.getCause().getMessage(), re.getCause());
			else
				throw new SystemException(re.getMessage(), re);
		}
	}

	public static List findAllByLanguage(Class clz, String locale, Order orderby)
			throws BusinessException {
		log.info(" findAllByLanguage(" + clz + "," + locale + ")");
		Criteria criteria = getSession().createCriteria(clz);
		criteria = criteria.add(Restrictions.eq("language", locale));
		try {
			if(orderby != null)
				return criteria.addOrder(orderby).list();
			else
				return criteria.list();
		} catch (Exception re) {
			if (re.getCause() instanceof java.sql.BatchUpdateException)
				throw new BusinessException(re.getCause().getMessage(), re.getCause());
			else
				throw new SystemException(re.getMessage(), re);
		}
	}

	public static List findAll(Class clz, Order orderby) throws BusinessException {
		log.debug("findAll("+clz.getName()+")");
		getSession().clear();
		Criteria criteria = getSession().createCriteria(clz);
//		criteria.setCacheable(arg0)
//		criteria.setCacheMode(arg0)
//		criteria.setFetchMode(arg0, arg1)
		
		try {
			if(orderby != null)
				return criteria.addOrder(orderby).list();
			else
				return criteria.list();
		} catch (RuntimeException re) {
			if (re.getCause() instanceof java.sql.BatchUpdateException)
				throw new BusinessException(re.getCause().getMessage(), re.getCause());
			else
				throw re;
		}
	}

	public static List findByExample(Object instance) throws BusinessException {
		return findByExample(instance, null);
	}

	/**
	 * 简单查询方法，查询条件为传入的对象的各属性值，即用此对象不为空的属性做为查询条件 例： XXObj.setName("xx");
	 * XXObj.setTime("2007-11-1"); findByExample(XXObj) 的条件是 name='xx' and
	 * time='2007-11-1'
	 * 
	 * @param instance  查询的实体对象
	 * @param orderby   排序对象
	 * @return
	 * @throws BusinessException
	 */
	public static List findByExample(Object instance, Order orderby) throws BusinessException {
		log.info(" findByExample(" + instance + ")");
		getSession().clear();
		Criteria criteria = getSession().createCriteria(instance.getClass());
		criteria = criteria.add(Example.create(instance));
		if (orderby != null) {
			criteria = criteria.addOrder(orderby);
		}
		setFetchMode(criteria,instance.getClass());
		try {
			return criteria.list();
		} catch (RuntimeException re) {
			if (re.getCause() instanceof java.sql.BatchUpdateException)
				throw new BusinessException(re.getCause().getMessage(), re.getCause());
			else
				throw re;
		}
	}

	/**
	 * 查询 条件为  property in（a，b，c）
	 * @param clz
	 * @param propertyName 字段
	 * @param values   限定的值范围
	 * @param orderby
	 * @return
	 * @throws BusinessException
	 */
	public static List findByPropertyIn(Class clz, String propertyName, List values, Order orderby)
			throws BusinessException {
		Criteria criteria = getSession().createCriteria(clz);	
		criteria.add(Restrictions.in(propertyName, values.toArray()) );
		setFetchMode(criteria,clz);
		if (orderby != null) {
			criteria = criteria.addOrder(orderby);
		}
		try {
			return criteria.list();
		} catch (RuntimeException re) {
			if (re.getCause() instanceof java.sql.BatchUpdateException)
				throw new BusinessException(re.getCause().getMessage(), re.getCause());
			else
				throw re;
		}
	}

	public static List findByProperty(Class clz, String propertyName, Object value, Order orderby)
			throws BusinessException {
		PropertyCondition[] pcs = new PropertyCondition[] { new PropertyCondition(propertyName,
				value, "=") };
		return findByPropertyConditions(clz, pcs, orderby);
	}

	public static List selectByPropertyForProjection(Class clz, String propertyName, Object value, Projection p)
			throws BusinessException {
		PropertyCondition[] pcs = new PropertyCondition[] { new PropertyCondition(propertyName,
				value, "=") };
		return selectByConditionsForProjection(clz, pcs, p);
	}

	public static List findByProperty(Class clz, String propertyName, Object value)
			throws BusinessException {
		PropertyCondition[] pcs = new PropertyCondition[] { new PropertyCondition(propertyName,
				value, "=") };
		return findByPropertyConditions(clz, pcs, null);
	}

	/**
	 * 组合条件查询，支持多条件的与查询
	 * 
	 * @param instance
	 * @param pConditions
	 * @param orderby
	 * @return
	 * @throws BusinessException
	 */
	public static List findByPropertyConditions(Class clz, PropertyCondition[] pConditions,
			Order orderby) throws BusinessException {
		log.info(" findByExample : " + clz + "pcondition.length=" + pConditions.length);
		Criteria criteria = createCriteria(clz, pConditions);		
		setFetchMode(criteria,clz);
		if (orderby != null) {
			criteria = criteria.addOrder(orderby);
		}
		try {
			return criteria.list();
		} catch (RuntimeException re) {
			if (re.getCause() instanceof java.sql.BatchUpdateException)
				throw new BusinessException(re.getCause().getMessage(), re.getCause());
			else
				throw re;
		}
	}

	/**
	 * 使用组合条件查询聚合结果集
	 * 返回查询函数结果，如 count,sum,max，avg...
	 * @param clz 查询对象
	 * @param pConditions 组合条件
	 * @param p  聚合条件函数
	 * 例如
<br>	    Projections.count("id")  
<br>		Projections.rowCount()  
<br>		Projections.avg("weight")  
<br>		Projections.max("age") 
<br>		Projections.groupProperty("color") 
	 * @return
	 * @throws BusinessException
	 */
	public static List selectByConditionsForProjection(Class clz, PropertyCondition[] pConditions,
			Projection p) throws BusinessException {
		log.debug(" selectByConditionsForProjection : " + clz + "Projection=" + p);
		Criteria criteria = createCriteria(clz, pConditions);
		try {
			criteria.setProjection(p);
			setFetchMode(criteria,clz);
			return criteria.list();
		} catch (RuntimeException re) {
			if (re.getCause() instanceof java.sql.BatchUpdateException)
				throw new BusinessException(re.getCause().getMessage(), re.getCause());
			else
				throw re;
		}
	}

	public static Criteria createCriteria(LogicalExpression expr,Class clz){
		Criteria criteria = getSession().createCriteria(clz);;
		return criteria.add(expr);
	}
	
	/**
	 * 由info存储的信息构建一个查询器 一般在内存或其它地方持久存放 CriteriaInfo对象，就可随时转换成可用的查询器
	 * 
	 * @param instance
	 * @param pConditions
	 * @param orderby
	 * @return
	 * @throws BusinessException 
	 */
	public static Criteria getCriteriaFromInfo(CriteriaInfo info) throws BusinessException {
		BaseHibernateDAO.getSession().clear();
		Criteria criteria = createCriteria(info.getClz(), info.getPConditions());
		if (info.getOrderby() != null) {
			criteria = criteria.addOrder(info.getOrderby());
		}
		setFetchMode(criteria,info.getClz());
		return criteria;
	}

	/**
	 * 
	 * like(String propertyName, Object value)：属性值与目标值满足模式匹配 lt(String
	 * propertyName, Object value)：属性值小于目标值 le(String propertyName, Object
	 * value)：属性值小于等于目标值 gt(String propertyName, Object value)：属性值大于目标值
	 * ge(String propertyName, Object value)：属性值大于等于目标值
	 * 
	 * @param propertyName
	 * @param value
	 * @param orderby
	 * @param op
	 * @return
	 */
	public static Criterion getCriterion(String propertyName, Object value, String op) {
		if (PropertyCondition.OPERATOR_EQ.equals(op)){
			if(value == null)
				return Restrictions.isNull(propertyName);
			else
				return Restrictions.eq(propertyName, value);
		}else if (PropertyCondition.OPERATOR_GE.equals(op))
			return Restrictions.ge(propertyName, value);
		else if (PropertyCondition.OPERATOR_GT.equals(op))
			return Restrictions.gt(propertyName, value);
		else if (PropertyCondition.OPERATOR_LE.equals(op))
			return Restrictions.le(propertyName, value);
		else if (PropertyCondition.OPERATOR_LT.equals(op))
			return Restrictions.lt(propertyName, value);
		else if (PropertyCondition.OPERATOR_LIKE.equals(op))
			return Restrictions.like(propertyName, value);
		else if (PropertyCondition.OPERATOR_ISNULL.equals(op))
			return Restrictions.isNull(propertyName);
		else if (PropertyCondition.OPERATOR_IN.equals(op))
			return Restrictions.in(propertyName,(Object[])value);
		else
			return null;
	}

	/**
	 * 根据传入条件构造指定class 的查询器
	 * @param clz   指定的查询对象class
	 * @param pConditions  查询条件
	 * @return
	 * @throws BusinessException
	 */
	private static Criteria createCriteria(Class clz,PropertyCondition[] pConditions) throws BusinessException{
		Criteria criteria = getSession().createCriteria(clz);
		if (pConditions != null) {
			for (int i = 0; i < pConditions.length; i++) {
				String proptyName = pConditions[i].getPropertyName();
				Object proptyValue = pConditions[i].getPropertyValue();
				String op = pConditions[i].getRelationalOperator();
				if(proptyName == null)
					continue;
				if(proptyName.indexOf(".") > 0){
					try {
						Criterion se = getRestrictionsIn( clz, proptyName, proptyValue, op);
						if(se == null){
							se = Expression.sql("1=2");
						}
						criteria = criteria.add(se);
						log.debug("查询条件: "+ se);
					} catch (Exception e) {
						throw new BusinessException("不能取得多层属性的过滤条件 "+e.getCause(),e);
					}
				}else{
					Criterion  se = getCriterion(proptyName,proptyValue,op);
					criteria = criteria.add(se);
					log.debug("查询条件: "+ se);
				}
			}
		}
		return criteria;
	}

	/**
	 * 取对象极联的查询条件（对象的属性嵌套多层）
	 * 多层嵌套，运算完返回一个 select in （集合）  的条件
	 * 例如： 查询 产品，条件是产品的所属公司的的注册用户的所在国家是 中国
	 *       查询逻辑 Product.class:: "company.user.country = 中国 
	 * @param parent   查询的对象，比如 Product.class
	 * @param propStr  嵌套的对象属性字符串，比如 "company.user.country" 
	 * @param lastValue  最后一个属性的匹配值  如  中国
	 * @param op         最后一个属性与匹配值的逻辑运算符，如 = > < ，参见 PropertyCondition.OPERATOR_xx
	 * @return
	 * @throws NoSuchFieldException 
	 * @throws SecurityException 
	 * @throws Exception
	 */
	private static Criterion getRestrictionsIn(Class parent,String propStr,Object lastValue,String op) throws SecurityException, NoSuchFieldException{
		if(propStr.indexOf(".") < 0){ 
			return getCriterion(propStr, lastValue, op);
		}else{
			String proptyName = propStr.substring(0,propStr.indexOf("."));
			Class subClass = parent.getDeclaredField(proptyName).getType();
			String nextString = propStr.substring(propStr.indexOf(".") + 1,propStr.length());
			Criterion resIn = getRestrictionsIn(subClass,nextString,lastValue,op);
			if(resIn == null){
				return null;
			}
			Criteria cri = getSession().createCriteria(subClass).add(resIn);
			List inList = cri.list();
			if(inList == null || inList.size() < 1){
				log.debug("当条件为："+ resIn + " 时没有匹配数据");
				return null;
			}
			return Restrictions.in(proptyName, cri.list().toArray());
		}
	}

	
	public static Object merge(Object detachedInstance) throws BusinessException {
		try {
			Object result = getSession().merge(detachedInstance);
			return result;
		} catch (RuntimeException re) {
			if (re.getCause() instanceof java.sql.BatchUpdateException)
				throw new BusinessException(re.getCause().getMessage(), re.getCause());
			else
				throw re;
		}
	}

	public static void attachDirty(Object instance) throws BusinessException {
		try {
			getSession().saveOrUpdate(instance);
		} catch (RuntimeException re) {
			if (re.getCause() instanceof java.sql.BatchUpdateException)
				throw new BusinessException(re.getCause().getMessage(), re.getCause());
			else
				throw re;
		}
	}

	public static void attachClean(Object instance) throws BusinessException {
		try {
			getSession().lock(instance, LockMode.NONE);
		} catch (RuntimeException re) {
			if (re.getCause() instanceof java.sql.BatchUpdateException)
				throw new BusinessException(re.getCause().getMessage(), re.getCause());
			else
				throw re;
		}
	}

	private static void setFetchMode(Criteria criteria,Class clz){
//		Field[] fs = clz.getDeclaredFields()	;
//		for(int i=0;i<fs.length;i++){
//		if("com.kylin.common.dict.orm.Dict".equals(fs[i].getType().getName())){
////log.debug("FetchMode.JOIN: "+fs[i].getName());
//		criteria.setFetchMode(fs[i].getName(), FetchMode.JOIN);
//		}
//		}
	}
}